#include <windows.h>

#include "lib.h"

#include "slotconn.h"
#include "progconn.h"
#include "slotguts.h"
#include "slotopts.h"
#include "slotspin.h"
#include "sltsql.h"
#include "log.h"
#include "endian.h"

#include "stdncerr.h"
#include "opts.h"
#include "SlotsXMLTranslator.h"
//
// balance is included in EVERY message
//


SlotConnection::SlotConnection(ProgressiveServer* server, SOCKET client)
	: ProgressiveConnection(server, client)
{
	loggedOn = false;
	guts = (SlotGuts*)0;
    info = new Info();
    m_bHasMoreSpinsToPack = false;

    // kharmon.  11-15-06.  #16921.  Zero out buffers before using them.
    msglen = 0;
    memset(msgbuf, 0, sizeof(msgbuf));    
}

SlotConnection::~SlotConnection()
{
	// CSN-7075: New Authentication
	trackLogoutSession();
	
	removeCasinoSpecific();
    delete guts;
    delete info;
}


void
SlotConnection::SendInt32(int32 data)
{
    msglen += Endian::SendInt4(data, &msgbuf[msglen]);
}

void
SlotConnection::SendUint16(uint16 data)
{
    msglen += Endian::SendInt2(data, &msgbuf[msglen]);
}



void
SlotConnection::SendUint8(uint8 data)
{
    msglen += Endian::SendByte(data, &msgbuf[msglen]);
}

uint8
SlotConnection::SendString( char* s )
{
    uint8 len = 1;
	if (s==NULL)
	{
		SendUint8(0);
	}
    else
    {
    	len = strlen(s);
    	if (len >= (sizeof( msgbuf ) - msglen))
        {
            len = 0;
        }
        else
        {
        	SendUint8(len);
        	for (int i=0; i<len; i++)
            {
        		SendUint8(s[i]);
            }
        }
    }

	return len+1;
}



void
SlotConnection::msgStart()
{
	msglen = 0;
}

void
SlotConnection::msgAdd(uint8 Byte)
{
	msgbuf[msglen++] = Byte;
}

uint8*
SlotConnection::msgGetBuf()
{
	return msgbuf + msglen;
}

void
SlotConnection::msgReleaseBuf(int32 Amt)
{
	msglen += Amt;
}

void
SlotConnection::msgEnd()
{
	ASSERT(msglen + oused + 2 < CONNECT_OBUF);

	msglen = encode(msgbuf, obuf + oused + 2, msglen);
	msglen += 2;	// include msg len bytes
	obuf[oused] = (uint8)(msglen & 0xff);
	obuf[oused + 1] = (uint8)((msglen >> 8) & 0xff);
	oused += msglen;
}

void
SlotConnection::sendMalfunctionMsg()
{
	msgStart();
	msgAdd((uint8)'E');
	msgAdd(0x04);
	msgEnd();
	Log::msgWarning("SlotConnection - sending malfunction message");
}

// FogBugz 753 - GChapman
// Added new error message for malfunction condition during login.
void
SlotConnection::sendLoginMalfunctionMsg()
{
	msgStart();
	msgAdd((uint8)'E');
	msgAdd(LOGINERR_MALFUNCTION);
	msgEnd();
	Log::msgWarning("SlotConnection - sending login malfunction message");
}

void
SlotConnection::sendStopMsg()
{
	msgStart();
	msgAdd((uint8)'!');
	msgEnd();
}

bool8
SlotConnection::handleJackpotMsg()
{
    int32	jackpot;
	int32	rate;

	getProgressiveData(&jackpot, &rate);

	msgStart();
	SendUint8('J');
	SendInt32(jackpot);
	SendInt32(rate);
	msgEnd();

    return true;
}

bool8 
SlotConnection::handleSavedGameMsg()
{
	if(guts->loadSavedGame())
	{
		msgStart();
		SendUint8('V');
        SendUint16(guts->getInfo()->creditValue);
		SendUint8(guts->getSlotSpinCount());

		/**
		 * Pack each spin to send
		 */
		for ( uint8 spinid = 0; spinid < guts->getSlotSpinCount(); spinid++ )
		{
			SlotSpin *spin = guts->getSlotSpin(spinid);

			if ( !spin->isSpinSent() )
			{
				SendUint8(spin->getStopCount());

				for ( uint8 reelid = 0; reelid < spin->getStopCount(); reelid++ )
				{
					SendUint8(spin->getStops()[reelid]);
				}

				/**
				 * Write the lines.
				 */
				SendUint8(spin->getSpinLineCount());
				/**
				 * Lines are 1 based.
				 */
				for ( int lineid = 1; lineid <= spin->getSpinLineCount(); lineid++ )
				{
					const SpinLine *line = spin->getSpinLineByLineId(lineid);

					SendUint8(line->lineid);
					SendUint8(line->payouttypeid);
					SendInt32(line->payoutamount);
					SendUint8(line->jackpot);
				}

				/**
				 * Write the scatters.
				 */
				SendUint8(spin->getNumScatters());

				for ( int scatterIndex = 0; scatterIndex < spin->getNumScatters(); scatterIndex++ )
				{
					const SlotScatter *pScatter = spin->getScatter(scatterIndex);

                    if ( pScatter )
                    {
					    SendInt32(pScatter->scatterposition);
					    SendUint8(pScatter->payouttypeid);
					    SendInt32(pScatter->payoutamount);
					    SendUint8(pScatter->jackpot);
                    }
                    else
                    {
                        ASSERT(false);
					    SendInt32(0);
					    SendUint8(0);
					    SendInt32(0);
					    SendUint8(0);
                    }
				}

				spin->setSpinSent(true);
			} //each spin
		} //all the spins
		msgEnd();
	} //if there is a save dgame

	return true;
}

bool8 
SlotConnection::handleGutsMsg()
{
    bool8 rc;

    rc = handleGutsDenominationsMsg();

    if ( rc )
    {
        rc = handleGutsPaylinesMsg();
    }

    if ( rc )
    {
        rc = handleGutsPayoutsMsg();
    }

    return rc;
}

bool8
SlotConnection::handleGutsIconNameMsg()
{
    ///@todo Write this method.
    return true;
}

bool8
SlotConnection::handleGutsReelsMsg()
{
    msgStart();
    SendUint8('R');
    SendUint8(guts->getReelCount());

    for ( uint8 i = 0; i < guts->getReelCount(); i++ )
    {
        SendUint8(guts->getIconCount(i));

        for ( uint8 j = 0; j < guts->getIconCount(i); j++ )
        {
            SendUint8( guts->getIcon(i, j)->iconid );
        }
    }

    msgEnd();

    return true;
}

bool8
SlotConnection::handleGutsPayoutsMsg()
{
    msgStart();
    SendUint8('P');
    SendUint8(guts->getBetCount());

    for ( uint8 i = 1; i <= guts->getBetCount(); i++ )
    {
        SendUint8(guts->getPayoutCount(i));

        for ( uint8 j = 0; j < guts->getPayoutCount(i); j++ )
        {
            SendInt32(guts->getPayout(i, j)->payoutAmount);
        }
    }

    msgEnd();

    return true;
}

bool8
SlotConnection::handleGutsDenominationsMsg()
{
    msgStart();
    SendUint8('D');
    SendUint8(guts->getDenominationCount());

    for ( uint8 nDenomination = 0; nDenomination < guts->getDenominationCount(); nDenomination++ )
    {
        SlotGameDenomination* pDenomination = guts->getDenomination(nDenomination);
        SendUint16(pDenomination->m_machineSubID);
        SendUint16(pDenomination->m_creditValue);
        SendUint8(pDenomination->m_isDefault);
    }

    msgEnd();

    return true;
}

bool8
SlotConnection::handleGutsPaylinesMsg()
{
    msgStart();
    SendUint8('Y');
    SendUint8(guts->getPaylineCount());
    SendUint8(guts->getReelCount());

    for ( uint8 paylineNum = 1; paylineNum <= guts->getPaylineCount(); paylineNum++ )
    {
        for ( uint8 reelNum = 0; reelNum < guts->getReelCount(); reelNum++ )
        {
            uint8 position = guts->getPaylinePosition(paylineNum, reelNum);
            SendUint8(position);
        }
    }

    msgEnd();

    return true;
}


bool8
SlotConnection::handleLoginMsg(bool isReload)
{
    if (isServerStopping())
    {
        sendStopMsg();
    }
    else
    {
        if (!loggedOn || isReload)	// CSN-14779: if we are reloading, we are logged on but we have to perform all login steps
        {
			if (!trackGetPlayerLimits())
			{
				msgStart();
				SendUint8('N');
				msgEnd();

				info->creditValue = 0;

				Log::msgWarning("Login: bad player limit. ");
			}

            if (!trackGetBalance())
            {
                msgStart();
                SendUint8('N');
                msgEnd();

                info->creditValue = 0;

				Log::msgWarning("Login: bad player balance");
            }
            else
            {
				if (guts)	// CSN-14779: we have to remove allocated guts, if exists, for the case of Reload message
					delete guts;
                guts = SlotGuts::createGuts(info->machineID);
    
                if ( guts && guts->load(info) )
                {
					this->setProgressiveJackpotID(guts->getJackpotId());
					this->setProgressiveOn(guts->isProgressive());
                    this->pProgServ->loadJackpot(guts->getJackpotId(), this->getForRealFlag(), this->getProgressiveFixedAmountMultiplier());
                    
                    handleGutsMsg();
                    handleSavedGameMsg();
                    handleSubgameMsg();
                    handleJackpotMsg();

                    // kharmon.  12-14-05.  #8140.  This message needs to be sent after the 
                    // previous messages because even though it was being sent during the call
                    // to handleGutsMsg(), the client USUALLY processed it last.  BUT, if the 
                    // network broke these messages up just right, it would INTERMITTENTLY get 
                    // processed out of the normal order...and when this rare event happened
                    // some games (e.g., Super Diamond Mine) would break.
                    // 
                    // SO, since it NORMALLY gets processed last anyway, let's just make sure 
                    // that this ALWAYS happens by sending it last.
                    //
                    // Note that the reason it normally got processed last is because it was posted
                    // to the event queue, whereas the other messages are handled immediately and thus
                    // would complete before the Guts msg got pulled back off the event queue.
                    handleGutsReelsMsg();
                                        
                    msgStart();
                    if (isReload)			// CSN-14779: reload denomination message is the same than the Login one, but with a different message ID
						SendUint8('r');
					else
						SendUint8('L');
                    SendInt32(info->balance);
                    SendUint8(guts->getBetCount());
                    msgAdd(guts->isProgressive());
                    msgAdd(guts->getFastPlayEnabled());
                    msgEnd();

                    loggedOn = true;

                    if(guts->isMalfunction())
					{
						sendLoginMalfunctionMsg();
					}
				
				}
                else
                {
                    Log::msgError("Login: bad slot guts: %d  ", info->machineID);
    
                    msgStart();
                    msgAdd((uint8)'N');
                    msgEnd();
    
                    delete guts;
                    guts = (SlotGuts *) 0;
                    info->creditValue = 0;
                }
            }
        }
        else
        {
            Log::msgWarning("Login: attempt to re-log on  ");

			// REVISIT: SNA Vulnerability: #2002120901
            msgStart();
            msgAdd((uint8)'N');
            msgEnd();
        }
    }

    return true;
}



bool8
SlotConnection::handleSubgameMsg()
{
	// [AO 2008-01-16] HA: verify the user is still logged on
	if (!ValidateSession())
		return true;

    msgStart();
    SendUint8('G');

	uint8	datasize = guts->getSubGameSize();
    SendUint8(datasize);

    /**
     * Note that even if the subgame data gets out of order
     * in the array (for example, data defined by id 4 might
     * be in index position 0) this will send them to the client
     * in the correct order because we ask for it based on ID and
     * send it in that order.
     */

    if ( datasize > 0 )
    {
	    SubGame *subGame = guts->getSubGame();
        int32 data;

        for (uint8 i = 0; i < datasize; i++ )
        {
            if ( !subGame->isServerOnly(i) )
            {
                data = subGame->getData(i);
            }
            else
            {
                data = 0;
            }
            SendInt32(data);
        }
    }

    msgEnd();
    return true;
}


/*//////////////////////////////////
	HANDLESPINMSG
	handle the player spin request
//////////////////////////////////*/
bool8
SlotConnection::handleSpinMsg()
{
	//rule1: gotta be logged on
    if (!loggedOn)
    {
        Log::msgWarning("Spin: not logged on...invalid bet  ");
        msgStart();
        SendUint8('B');
        msgEnd();
		return true;
    }

	//rule2: server cannot be going down
	if (isServerStopping())
	{
		sendStopMsg();
		return true;
	}

	// [AO 2008-01-16] HA: verify the user is still logged on
	if (!ValidateSession())
		return true;

	//now parse some necessary message parameters
	int32 msgBufIdx = 1;
	uint8 lineCount = msgbuf[msgBufIdx++];
    Endian::ReceiveInt2(&info->creditValue, &msgbuf[msgBufIdx]);
    msgBufIdx+=2;
	uint8 bet		= msgbuf[msgBufIdx++];

	if((lineCount + msgBufIdx) > m_nDecodedMsgLen)
	{
		// kharmon.  08-17-05.  #4962, #5116.  Make sure that the message is valid with regards
		// to the number of lines that we're supposed to evaluate.  Passing in a bogus linecount 
		// could trigger an access violation.  Report back with "X"...which will result in an invalid
		// action error.
		Log::msgWarning("Spin:  Faulty linecount causing reads past end of message buffer.");
        msgStart();
        SendUint8('X');
        msgEnd();

		return true;
	}

	//look at the player balance in the database
	trackGetBalance();

	//convert the player balance to machine credits
	int32 balCredits = centsToCredits(info->balance);

    bool bCreditValueInvalid = true;

    // Ensure the credit value passed from the client
    // is valid for this machine.
    for ( uint8 nDenomination = 0; nDenomination < guts->getDenominationCount(); nDenomination++ )
    {
        SlotGameDenomination* pDenomination = guts->getDenomination(nDenomination);

        if ( pDenomination->m_creditValue == info->creditValue)
        {
            bCreditValueInvalid = false;
            break;
        }
    }    

    if ( bCreditValueInvalid )
    {
        // There is a problem with the credits being sent from the client.
		Log::msgWarning("Spin: invalid credits");
		msgStart();
		msgAdd((uint8)'$');
		msgEnd();
		return true;
    }

	//rule3: must have enough balance to cover the bet
	if (bet > 0)
    {
	    if (balCredits < 1)
	    {
		    // player balance is too low
		    Log::msgWarning("Spin: out of money  ");
		    msgStart();
		    msgAdd((uint8)'$');
		    msgEnd();
		    return true;
	    }
    }

	//parse the remaining message parameters
	uint8 *lines = new uint8[lineCount];
	for ( uint8 i = 0; i < lineCount; i++ )
    {
		lines[i] = msgbuf[msgBufIdx + i];
    }

	//rule4: cannot play more coins than the machine maximum
	if ( bet > guts->getBetCount() )
    {
		bet = guts->getBetCount();
    }

	//rule5: cannot play more coins than you have
	if (bet*lineCount > balCredits)
    {
		    Log::msgWarning("Spin: out of money  ");
		    msgStart();
		    msgAdd((uint8)'$');
		    msgEnd();
		    return true;
    }

	//rule6: cannot play if you have a malfunction
	if (guts->isMalfunction())
    {
		Log::msgWarning("Spin:Already Malfunction");
		sendMalfunctionMsg();
		return true;
	}

	//spin it baby!
	executeSpinMsg(bet, lines, lineCount);

    delete [] lines;

	//success
    return true;
}


void
SlotConnection::executeSpinMsg(uint8 bet, uint8 *lines, uint8 lineCount)
{
    int32 payout;
    SlotSpin *spin=NULL;
	bool8 MBerror = false;
	bool8 jackpotsuccess = true;
    int32 multibankPayout = 0;


    if ( guts->isValidBet(bet) )
    {
        int32 multibankBetCents = 0;

       if ( guts->isProgressive())
        {
           if(bet > 0 && !placeProgressiveBet(bet * info->creditValue* lineCount, bet*lineCount, multibankBetCents))
		   {
				//Can't make progressive bet - refund the money and error out
				MBerror = true;
				Log::msgWarning("Spin: Error placing multibank bet (out of funds?)");
				msgStart();
				msgAdd((uint8)'E');
				msgAdd((uint8)0);
		        msgEnd();

		   }
        }


		if(!MBerror)
		{
			guts->spin(bet, multibankBetCents, lines, lineCount, 0);

			/**
			 * Go through the lines looking for progressive winners.
			 */
			for ( uint8 spinid = 0;  spinid < guts->getSlotSpinCount(); spinid++ )
			{
				spin = guts->getSlotSpin(spinid);

				for ( uint8 lineidx = 0; lineidx < spin->getSpinLineCount(); lineidx++ )
				{
					const SpinLine *spinLine = spin->getSpinLineAt(lineidx);

					
	#ifdef DEBUGSPIN
					if (lineidx == 0 && guts->isForcedJackpot())	
					{
						spin->setJackpotAmount(spinLine->lineid,0); //FORCE A JACKPOT
					}
	#endif
		
					if ( spinLine->jackpot != 0 && this->isProgressiveOn() )
					{
						jackpotsuccess = this->pProgServ->setProgressiveWinner(guts->getJackpotId(), 100, guts->isForMoney(), this->getProgressiveFixedAmountMultiplier(), &payout, &multibankPayout);
						spin->setJackpotAmount(spinLine->lineid, payout);
						spin->setMultibankPayout(multibankPayout);
					}

				}

                SlotScatter* pScatter = NULL;
                for ( uint8 scatteridx = 0; scatteridx < spin->getNumScatters(); scatteridx++ )
                {
                    pScatter = spin->getScatter( scatteridx );

                    if ( pScatter->jackpot != 0 && isProgressiveOn() ) 
                    {
						jackpotsuccess = this->pProgServ->setProgressiveWinner(guts->getJackpotId(), 100, guts->isForMoney(), this->getProgressiveFixedAmountMultiplier(), &payout, &multibankPayout);
                        pScatter->payoutamount = payout;
                        spin->setMultibankPayout(multibankPayout);
                    }
                }
			}

        /**
         * Write the winners.
         */
			if ( jackpotsuccess && guts->saveGame() )
			{

				if ( guts->saveSpins() )
				{
	 				guts->saveSubGameData();
					handleSubgameMsg();
					packSpins();

					if ( !guts->isMalfunction() )
					{
						if ( guts->isGameComplete() )
						{
							guts->completeGame();
							handleJackpotMsg();
						}
					}
					else
					{
						sendMalfunctionMsg();
					}
				}
				else
				{
					// Unable to save the spins
					//
					msgStart();
					msgAdd((uint8)'E');
					msgAdd((uint8)0);
					msgEnd();
					ASSERT(0);
				}
			}
			else
			{
				// Unable to save the game
				//
				msgStart();
				msgAdd((uint8)'E');
				msgAdd((uint8)0);
				msgEnd();
				ASSERT(0);
			}
		}
    }
    else
    {
        Log::msgWarning("Spin: invalid bet  ");

        msgStart();
        msgAdd((uint8)'E');
        msgAdd((uint8)0);
        ASSERT(0);
    }
}

void
SlotConnection::packSpins()
{
    if ( guts->getSlotSpinCount() > 0 )
    {
        uint8 spinCount = 0;
        uint8 startingSpinIndex = 0;
        uint8 unsentSpinCount = 0;
        uint8 spinsToSend = 0;
        uint8 spinResponseType = 0;
        bool bStartingIndexFound = false;

        /** 
         * find the number of spins ready to be sent.
         */
        for ( uint8 spinid = 0; spinid < guts->getSlotSpinCount(); spinid++ )
        {
            SlotSpin *spin = guts->getSlotSpin(spinid);

            if ( spin->isSpinSaved() )
            {
                spinCount++;
            }
            
            // If this spin has not been sent...
            if ( !spin->isSpinSent() )
            {
                // Bump the unsent spin count
                unsentSpinCount++;

                // And if we have not determined the starting index for spins...
                if ( !bStartingIndexFound )
                {
                    bStartingIndexFound = true;
                    startingSpinIndex = spinid;
                }
            }
        }

        // Calculate the number of spins sent with this
        // message
        if ( unsentSpinCount > MAX_SPINS_PER_MSG )
        {
            spinsToSend = MAX_SPINS_PER_MSG;
        }
        else
        {
            spinsToSend = unsentSpinCount;
        }

        // Determine if this is a stand alone 
        // spin response, or one of a series of 
        // spin response messages.
        if ( spinCount <= MAX_SPINS_PER_MSG )
        {
            spinResponseType = SINGLE_SPIN_RESPONSE;
        }
        else if ( startingSpinIndex == 0 )
        {
            spinResponseType = FIRST_SPIN_RESPONSE;
        }
        else if ( startingSpinIndex + MAX_SPINS_PER_MSG >= spinCount )
        {
            spinResponseType = LAST_SPIN_RESPONSE;
        }
        else
        {
            spinResponseType = MIDDLE_SPIN_RESPONSE;
        }
        
        // The spin response type SHOULD have been
        // set to a non zero value above.
        ASSERT( spinResponseType );

		if ( spinCount == 0)
		{
			//unfinished spin message
			msgStart();
            SendUint8('s');
            SendInt32(info->balance);
            SendUint8(spinCount);
            msgEnd();
		}
		else if ( spinCount > 0 )
        {
            msgStart();
            SendUint8('S');
            SendInt32(info->balance);
            SendUint8(spinCount);
            SendUint8(spinsToSend);
            SendUint8(startingSpinIndex);
            SendUint8(spinResponseType);
    
            /**
             * Pack each spin and mark that it was sent.
             */
            for ( uint8 spinid = 0; spinid < guts->getSlotSpinCount(); spinid++ )
            {
                SlotSpin *spin = guts->getSlotSpin(spinid);
    
                if ( !spin->isSpinSent() )
                {
                    SendUint8(spin->getStopCount());
        
                    for ( uint8 reelid = 0; reelid < spin->getStopCount(); reelid++ )
                    {
                        SendUint8(spin->getStops()[reelid]);
                    }
        
                    /**
                     * Write the lines.
                     */
                    SendUint8(spin->getSpinLineCount());
                    /**
                     * Lines are 1 based.
                     */
                    for ( int lineid = 1; lineid <= spin->getSpinLineCount(); lineid++ )
                    {
                        const SpinLine *line = spin->getSpinLineByLineId(lineid);
        
                        SendUint8(line->lineid);
                        SendUint8(line->payouttypeid);
                        SendInt32(line->payoutamount);
                        SendUint8(line->jackpot);
                    }

				    /**
				     * Write the scatters.
				     */
				    SendUint8(spin->getNumScatters());

				    for ( int scatterIndex = 0; scatterIndex < spin->getNumScatters(); scatterIndex++ )
				    {
					    const SlotScatter *pScatter = spin->getScatter(scatterIndex);

                        if ( pScatter )
                        {
					        SendInt32(pScatter->scatterposition);
					        SendUint8(pScatter->payouttypeid);
					        SendInt32(pScatter->payoutamount);
					        SendUint8(pScatter->jackpot);
                        }
                        else
                        {
                            ASSERT(false);
					        SendInt32(0);
					        SendUint8(0);
					        SendInt32(0);
					        SendUint8(0);
                        }
				    }
                
                    spin->setSpinSent(true);

                    spinsToSend--;

                    if ( spinsToSend == 0 )
                    {
                        break;
                    }
                }
            }
            msgEnd();

            if ( spinResponseType == FIRST_SPIN_RESPONSE )
            {
                guts->setCanResetForNewGame(false);
                m_bHasMoreSpinsToPack = true;
            }
            else if ( spinResponseType == LAST_SPIN_RESPONSE )
            {
                m_bHasMoreSpinsToPack = false;
                guts->setCanResetForNewGame(true);
                guts->resetForNewGame();
            }
         }
    }
    else
    {
        /**
         * Shoudn't be here.  There should never be a case when spins
         * are looked for but none are found.
         */
        ASSERT(0);
    }
}

bool8
SlotConnection::handleBonusGameMsg()
{
	uint8 bonusGameType;
    int16 bonusDataCount;
    int32 *bonusData;
    int32 i;

	// [AO 2008-01-16] HA: verify the user is still logged on
	if (!ValidateSession())
		return true;

    /*  Collect the bonus game data */

	bonusGameType = msgbuf[1];
    Endian::ReceiveInt2(&bonusDataCount, &msgbuf[2]);
    bonusData = new int32 [bonusDataCount];

    for ( i = 0; i < bonusDataCount; i++ )
    {
        Endian::ReceiveInt4(&bonusData[i], &msgbuf[ i * 4 + 4]);
    }

    /* Let the guts do whatever it needs to it */
    guts->playBonusGame(bonusGameType, bonusDataCount, bonusData);


    delete [] bonusData;

    /* Send the subgame message in case the data changed */
    handleSubgameMsg();

    return true;
}

bool8
SlotConnection::handleAnsiLoginMsg()
{
	int32 err = 0;

	if (!parseAnsiCasinoLogin(8, &err))
	{
		msgStart();
		msgAdd((uint8)'E');
		msgAdd((int32) err);
		msgEnd();

		return true;
	}	

	return handleLoginMsg();
}

bool8
SlotConnection::handleUnicodeLoginMsg()
{
	int32 err = 0;

	if (!parseUnicodeCasinoLogin(9, &err))
	{
		msgStart();
		msgAdd((uint8)'E');
		msgAdd((int32) err);
		msgEnd();

		return true;
	}	

	return handleLoginMsg();

}

bool8 
SlotConnection::HandleReloadDenomination()	// CSN-14779: change denomination
{
	if (isServerStopping())
	{
		sendStopMsg();
		return true;
	}

	// [AO 2008-01-16] HA: verify the user is still logged on
	if (!ValidateSession())
		return true;	

    if (!parseReloadDenomination())
    {
        msgStart();
		msgAdd((uint8)'E');
		msgAdd((uint8)0);
		msgEnd();

		return true;
    }

	return handleLoginMsg(true);
}

bool8
SlotConnection::handleUnknownMsg()
{
	Log::msgWarning("Connection::getResponse:  Unknown input (0x%02x)  ",
		msgbuf[0]);

	// Server Network Audit: Vulnerability #2002120901
	//
#ifdef DEBUG				// FIX - SNA:Vulnerability #2002120901
	msgStart();
	msgAdd((uint8)'N');
	msgEnd();
#endif						// FIX - SNA:Vulnerability #2002120901

	return true;
}


bool8
SlotConnection::handlePreferedMachSubId()
{
	bool8  forMoney = false;
	String login;
	uint8  MachId = 0;
	uint16 MachSubId = 0;
	uint16  preferedCreditValue = 0;
	int32  i = 1;

	MachId = msgbuf[i++];
    i += Endian::ReceiveInt2(&preferedCreditValue, &msgbuf[i]);
	forMoney = msgbuf[i++];
	i += login.deserialize( &msgbuf[i] );

	trackPreferedMachSubId(MachId, preferedCreditValue, login, forMoney, &MachSubId);

	msgStart();
	SendUint8('F');
	SendUint16(MachSubId);
	msgEnd();

    return true;
}

bool8
SlotConnection::getResponse()
{
	int32	ieat	= 0;
	uint32	remain	= iused;
	uint8 *	src		= ibuf;
    bool8	rc		= false;
	uint32	message_length;

	// [AO 2007-10-01] We can send just one reply for certain repeating messages.
	// We're not handling partial buffers and incomplete messages but that's OK.
	char last_message = 0;

	// Slots utilizes a two byte size field, hence we need at least two bytes
	// for any given message.
	while (remain >= 2)
	{
		message_length = src[0] + (src[1] << 8);

		// Reject invalid message lengths of zero, one, or two - because they
		// do not contain payload data.
		if (message_length <= 2)
		{
			ieat	+= 2;
			remain	-= 2;
			src		+= 2;
			continue;
		}

		// Reject invalid message lengths which exceed the length of our
		// receive buffer.
		if (message_length > INMAX)
		{
			DEBUGMSG(("SlotConnection::getResponse:"
				"  message length exceeds buffer size  "));
			ieat = iused;
			break;
		}

		// Incomplete message - must wait for more data.
		if (remain < message_length)
			break;

        // kharmon.  11-15-06.  #16921.  Zero out buffers before using them.
	    msglen = 0;
	    memset(msgbuf, 0, sizeof(msgbuf));

		// Well-formed, but not necessarily valid, message.
		m_nDecodedMsgLen = decode(src + 2, msgbuf, message_length - 2);
		ieat	+= message_length;
		remain	-= message_length;
		src		+= message_length;

		if (!loggedOn)
		{
			switch (msgbuf[0])
			{
				case 'L':
					rc = handleAnsiLoginMsg();
					break;

				case 'F':
					rc = handlePreferedMachSubId();
					break;

				case 'M':
					switch (msgbuf[1])
					{
						case 'L':
							rc = handleUnicodeLoginMsg();
							break;
						default:
							rc = handleUnknownMsg();
							break;

					}
					break;

				case 'm':
					// It is possible to get this message when the player is
					// not logged in to the game.  However, since they
					// are not logged in, there will be no way to handle it.
					// So we just ignore it, rather than send Unknown message
					// which stops the game.
					break;				

				default:
					rc = handleUnknownMsg();
					break;
			}
		}
		else
		{
			switch (msgbuf[0])
			{
				case 'B':
					rc = handleBonusGameMsg();
					break;

				case 'G':
					rc = handleSubgameMsg();
					break;

				case 'J':
					// [AO 2007-10-01] Ignore repeating messages
					// [AO 2008-01-16] HA: verify the user is still logged on
					if (msgbuf[0] == last_message || !ValidateSession())
						rc = true;
					else
						rc = handleJackpotMsg();
					break;

				case 'S':
					rc = handleSpinMsg();
					break;

                case 's':
                    rc = handleGetNextSpinResponseMsg();
                    break;

				case 'm':  // Balance Request
					// [AO 2007-10-01] Ignore repeating messages
					// [AO 2008-01-16] HA: verify the user is still logged on
					if (msgbuf[0] == last_message || !ValidateSession())
						rc = true;
					else
						// balance Broadcast System (BBS)
						// client needs fresh balance to synchronize balance display
						rc = handleGetBalance();
					break;

				case 'r':	// Reload with a new denomination (CSN-13423)
					rc = HandleReloadDenomination();		
					break;

				default:
					rc = handleUnknownMsg();
					break;
			}
		}

		last_message = msgbuf[0]; // [AO 2007-10-01] Handle repeating messages
    }

	if (iused <= ieat) // [AO 2007-10-01] Avoid buffer overflow
		iused = 0;
	else
	{
		MoveMemory(ibuf, ibuf + ieat, iused - ieat);
		iused -= ieat;
	}

	// NOTE:
	//   The value returned by the *last* message handler is used as our
	//   return value.  This is because the last message handler has either
	//   a message queued in msgbuf (true), or has ready sent msgbuf (false).
	//   Thus, the last method is always our authoritative source.
	return rc;
}
		
int32
SlotConnection::creditsToCents(int32 cred)
{ 
    return cred * info->creditValue;
}
		
// truncates (floor) value since fractional credits not allowed
//
int32
SlotConnection::centsToCredits(int32 cents)
{
    return cents / info->creditValue;
}

// balance Broadcast System (BBS)
bool8
SlotConnection::handleGetBalance()
{
	if (isServerStopping())
	{
		sendStopMsg();
		return true;
	}

	//rule1: gotta be logged on
    if( loggedOn && trackGetBalance() )
    {
        msgStart();
        SendUint8('m');
        SendInt32(info->balance);
        msgEnd();
    }

	//success
    return true;
}

bool8
SlotConnection::handleGetNextSpinResponseMsg()
{
	//rule1: gotta be logged on
    if (!loggedOn)
    {
        Log::msgWarning("Get Next Spin: not logged on...invalid bet  ");
        msgStart();
        SendUint8('B');
        msgEnd();
		return true;
    }

	//rule2: server cannot be going down
	if (isServerStopping())
	{
		sendStopMsg();
		return true;
	}

	// [AO 2008-01-16] HA: verify the user is still logged on
	if (!ValidateSession())
		return true;

    if ( !m_bHasMoreSpinsToPack )
    {
        Log::msgWarning("Get Next Spin: No More Spins  ");
        msgStart();
	    msgAdd((uint8)'E');
	    msgAdd(0x07);
        msgEnd();
		return true;
    }

    packSpins();

	//success
    return true;
}

void 
SlotConnection::allocateXMLTranslator( XMLTranslator** target )
{
	*target = new SlotsXMLTranslator( getServer(), this );
}

// [AO 2008-01-16] Refactoring for HA
bool SlotConnection::ValidateSession()
{
	int32 session_check = trackValidateSession();
	if (!session_check) // session is valid
		return true;

	// [AO 2008-01-22] Additional case from RTG 6.6.0 code drop
	if (session_check == SlotsSQL::NormalLogout)
		session_check = LOGINERR_NOTLOGGEDIN;

	msgStart();
	msgAdd(static_cast<uint8>('N'));
	msgAdd(static_cast<int32>(session_check));
	msgEnd();
	return false;
}
